Skip to content

Conversation

@coodos
Copy link
Contributor

@coodos coodos commented Dec 15, 2025

Description of change

Issue Number

closes #553
closes #554
closes #555

Type of change

  • Update (a change which updates existing functionality)

How the change has been tested

Change checklist

  • I have ensured that the CI Checks pass locally
  • I have removed any unnecessary logic
  • My code is well documented
  • I have signed my commits
  • My code follows the pattern of the application
  • I have self reviewed my code

Summary by CodeRabbit

  • New Features
    • Added ability to set and manage maximum negative balance limits on currencies that allow negative balances.
    • Administrators can now configure and adjust balance floor thresholds through a new settings interface.
    • System validates transactions to prevent account balances from exceeding configured negative limits.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 15, 2025

Walkthrough

This pull request introduces maxNegativeBalance as a persistent field on currencies, enabling administrators to set limits on how far below zero account balances may fall. The feature spans database schema (new column via migration), API layer (new controller endpoint), business logic (validation in service and ledger), and client UI (management interface with save/clear actions).

Changes

Cohort / File(s) Summary
Database Schema
platforms/eCurrency-api/src/database/entities/Currency.ts, platforms/eCurrency-api/src/database/migrations/1765784749012-migration.ts
Adds maxNegativeBalance field to Currency entity as nullable decimal(18,2) column; introduces corresponding TypeORM migration for up/down schema changes.
API Layer
platforms/eCurrency-api/src/controllers/CurrencyController.ts, platforms/eCurrency-api/src/index.ts
Adds new updateMaxNegativeBalance controller method with auth checks and validation; registers PATCH endpoint /api/currencies/:id/max-negative.
Service Layer
platforms/eCurrency-api/src/services/CurrencyService.ts, platforms/eCurrency-api/src/services/LedgerService.ts
Extends createCurrency to accept optional maxNegativeBalance parameter; introduces updateMaxNegativeBalance service method with admin and validation checks; enhances transfer in LedgerService to validate balance limits and optimize balance queries.
Client UI
platforms/eCurrency/client/src/pages/currency-detail.tsx
Adds state management, input controls (range + numeric inputs), and save/clear functionality for max negative balance; integrates React Query cache invalidation post-update.

Sequence Diagram

sequenceDiagram
    actor User as Administrator
    participant UI as currency-detail.tsx
    participant API as CurrencyController
    participant Service as CurrencyService
    participant Ledger as LedgerService
    participant DB as Database

    User->>UI: Set max negative balance value
    UI->>API: PATCH /api/currencies/:id/max-negative {value}
    API->>Service: updateMaxNegativeBalance(id, value, admin)
    
    Note over Service: Validate:<br/>- Currency exists<br/>- Admin permission<br/>- allowNegative=true<br/>- Value is null or ≤0

    Service->>DB: Update currency.maxNegativeBalance
    DB-->>Service: Updated Currency
    Service-->>API: Currency entity
    API-->>UI: {id, maxNegativeBalance, ...}
    
    UI->>UI: Invalidate cache<br/>(currency, accountDetails)
    
    Note over User,Ledger: Later: User attempts transfer
    
    User->>API: POST /transfer {amount, ...}
    API->>Ledger: transfer(currencyId, amount, ...)
    
    Note over Ledger: Compute balance change<br/>if (maxNegativeBalance set<br/>AND balance+amount < limit)<br/>throw error
    
    alt Balance within limit
        Ledger->>DB: addLedgerEntry(...)
        DB-->>Ledger: Ledger entry
        Ledger-->>API: Success
    else Balance exceeds limit
        Ledger-->>API: Insufficient funds error
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • LedgerService modifications: Review the enhanced validation logic in transfer() to ensure maxNegativeBalance checks are correct and the new existingBalance parameter usage doesn't introduce balance-calculation discrepancies.
  • CurrencyService validation: Verify that updateMaxNegativeBalance enforces all required constraints (admin role, allowNegative check, numeric validation).
  • Client state synchronization: Confirm that cache invalidation strategy properly refreshes all dependent UI sections after updates.

Possibly related PRs

  • chore: move eCurrency #471: Directly related—this PR builds upon and extends the Currency, CurrencyController, CurrencyService, and migration patterns introduced there to add the new maxNegativeBalance management feature.

Suggested reviewers

  • sosweetham
  • xPathin
  • ananyayaya129

Poem

🐰 A hop, a skip, a balance to keep,
The burrow's new limit runs deep,
Max negative set with a careful paw,
No debt below this earned law,
Our ledgers now wiser, our wallets more fair! 🌙

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR addresses issue #554 (negative limit) comprehensively with maxNegativeBalance implementation. However, no evidence of work on #553 (hashing/timestamps) or #555 (burn money functionality) is found in the code changes. Verify whether issues #553 and #555 were intended to be included in this PR. If so, add the missing functionality; otherwise, remove them from the PR description.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title directly describes the main feature: adding max negative balance caps and enforcing ledger bounds, which aligns with the primary changes across the codebase.
Description check ✅ Passed The description includes required sections: issue numbers, type of change, and change checklist. However, the 'How the change has been tested' section is missing content and no testing details are provided.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the max negative balance feature referenced in the PR title and issue #554. No unrelated or out-of-scope modifications were detected.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/burn-and-negative-control

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
platforms/eCurrency-api/src/services/LedgerService.ts (1)

96-124: Wrap the transfer method in a database transaction to prevent concurrent balance validation violations.

There is a time-of-check to time-of-use (TOCTOU) gap between fetching currentBalance (line 96) and persisting ledger entries (lines 112-139). Concurrent transfers from the same account could both pass validation before either commits, potentially violating the maxNegativeBalance constraint.

Use AppDataSource.transaction() with proper isolation level (similar to the pattern in GroupService.ts), or implement row-level locking with FOR UPDATE to ensure balance validation and ledger creation are atomic.

🧹 Nitpick comments (4)
platforms/eCurrency-api/src/database/entities/Currency.ts (1)

39-40: TypeORM decimal columns return strings at runtime.

TypeORM's decimal type returns values as strings, not JavaScript numbers. The TypeScript type number | null is misleading and may cause subtle bugs when performing arithmetic or comparisons without explicit conversion.

Consider adjusting the type or using a transformer:

-    @Column("decimal", { precision: 18, scale: 2, nullable: true })
-    maxNegativeBalance!: number | null;
+    @Column("decimal", { precision: 18, scale: 2, nullable: true, transformer: {
+        to: (value: number | null) => value,
+        from: (value: string | null) => value === null ? null : parseFloat(value)
+    }})
+    maxNegativeBalance!: number | null;

Alternatively, update the type to string | null and convert explicitly at usage sites.

platforms/eCurrency-api/src/controllers/CurrencyController.ts (1)

162-166: Consider adding semantic validation for maxNegativeBalance.

The current validation only checks that the value is a valid number (or null). Given the semantic meaning (a floor for how negative a balance can go), consider validating that non-null values are <= 0:

             // Allow null to clear; otherwise must be a number
             const parsedValue = value === null || value === undefined ? null : Number(value);
             if (parsedValue !== null && Number.isNaN(parsedValue)) {
                 return res.status(400).json({ error: "Invalid value for maxNegativeBalance" });
             }
+            if (parsedValue !== null && parsedValue > 0) {
+                return res.status(400).json({ error: "maxNegativeBalance must be zero or negative" });
+            }
platforms/eCurrency-api/src/services/CurrencyService.ts (1)

20-47: Align createCurrency maxNegativeBalance invariants with update path

createCurrency now accepts and persists maxNegativeBalance but does not enforce the same constraints you added in updateMaxNegativeBalance (must only be set when allowNegative is true, must be null or <= 0, not NaN). That means callers can create a currency in a state that the update path would later reject (e.g. allowNegative = false with a non‑null cap, or a positive cap).

To keep invariants consistent and avoid surprising states, consider normalizing and validating here as well, e.g.:

     // Generate eName (UUID with @ prefix)
     const ename = `@${uuidv4()}`;
 
+    const normalizedMaxNegativeBalance =
+        allowNegative && maxNegativeBalance !== null ? maxNegativeBalance : null;
+
+    if (normalizedMaxNegativeBalance !== null) {
+        if (Number.isNaN(normalizedMaxNegativeBalance)) {
+            throw new Error("Invalid max negative balance value");
+        }
+        if (normalizedMaxNegativeBalance > 0) {
+            throw new Error("Max negative balance must be zero or negative");
+        }
+    }
+
     const currency = this.currencyRepository.create({
         name,
         description,
         ename,
         groupId,
         createdBy,
         allowNegative,
-        maxNegativeBalance,
+        maxNegativeBalance: normalizedMaxNegativeBalance,
     });
platforms/eCurrency/client/src/pages/currency-detail.tsx (1)

292-301: Optional: Format maxNegativeBalance consistently with other monetary values

Here you display maxNegativeBalance with a bare toLocaleString(), while balance and total supply are formatted with two decimal places. For consistency and readability (especially since the DB column is decimal with scale), consider aligning the formatting:

-                  {currency.allowNegative
-                    ? (currency.maxNegativeBalance !== null && currency.maxNegativeBalance !== undefined
-                      ? Number(currency.maxNegativeBalance).toLocaleString()
-                      : "No cap")
-                    : "Not applicable"}
+                  {currency.allowNegative
+                    ? (currency.maxNegativeBalance !== null && currency.maxNegativeBalance !== undefined
+                      ? Number(currency.maxNegativeBalance).toLocaleString(undefined, {
+                          minimumFractionDigits: 2,
+                          maximumFractionDigits: 2,
+                        })
+                      : "No cap")
+                    : "Not applicable"}

Not required for correctness, but it keeps the UI consistent with the way balances and total supply are shown.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dbefb26 and 28a6b7b.

📒 Files selected for processing (7)
  • platforms/eCurrency-api/src/controllers/CurrencyController.ts (7 hunks)
  • platforms/eCurrency-api/src/database/entities/Currency.ts (1 hunks)
  • platforms/eCurrency-api/src/database/migrations/1765784749012-migration.ts (1 hunks)
  • platforms/eCurrency-api/src/index.ts (1 hunks)
  • platforms/eCurrency-api/src/services/CurrencyService.ts (3 hunks)
  • platforms/eCurrency-api/src/services/LedgerService.ts (3 hunks)
  • platforms/eCurrency/client/src/pages/currency-detail.tsx (6 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
platforms/eCurrency-api/src/database/migrations/1765784749012-migration.ts (1)
platforms/eCurrency-api/src/database/migrations/1765039149572-migration.ts (1)
  • Migration1765039149572 (3-14)
platforms/eCurrency-api/src/index.ts (1)
platforms/eCurrency-api/src/middleware/auth.ts (1)
  • authGuard (36-41)
platforms/eCurrency/client/src/pages/currency-detail.tsx (1)
platforms/eCurrency/client/src/lib/apiClient.ts (1)
  • apiClient (5-10)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (8)
platforms/eCurrency-api/src/database/migrations/1765784749012-migration.ts (1)

1-14: LGTM!

The migration correctly adds and removes the maxNegativeBalance column with the appropriate type (numeric(18,2)), matching the entity definition and following the established migration pattern in the codebase.

platforms/eCurrency-api/src/services/LedgerService.ts (2)

43-49: LGTM!

The existingBalance parameter is a sensible optimization that avoids a redundant database query when the caller has already fetched the balance for validation purposes.


103-108: Consider validating maxNegativeBalance is non-positive.

The current logic assumes maxNegativeBalance represents a floor (e.g., -100). If a positive value is accidentally set, the condition newBalance < maxNegativeBalance would never trigger for typical debits leading to negative balances. Consider adding validation in the controller/service to ensure maxNegativeBalance <= 0 when set.

platforms/eCurrency-api/src/index.ts (1)

115-115: LGTM!

The new PATCH endpoint follows the existing route patterns, uses appropriate HTTP semantics for a partial update, and is correctly protected with authGuard.

platforms/eCurrency-api/src/controllers/CurrencyController.ts (2)

18-18: LGTM!

The maxNegativeBalance field is correctly extracted from the request body, passed to the service, and included in the response. The implementation follows the existing patterns for other currency fields.

Also applies to: 29-29, 40-40


174-185: LGTM!

The response structure is consistent with other currency endpoints, including all relevant fields.

platforms/eCurrency-api/src/services/CurrencyService.ts (1)

87-117: Max negative balance update flow and validation look solid

The new updateMaxNegativeBalance method correctly:

  • Verifies the currency exists and the requester is a group admin.
  • Disallows setting a cap when allowNegative is false.
  • Ensures the value is either null or a non‑NaN number that is <= 0.

This keeps the persisted state consistent with the business rules around negative balances.

platforms/eCurrency/client/src/pages/currency-detail.tsx (1)

1-1: State and effect wiring for max negative balance looks consistent

The added React Query client usage, MAX_NEGATIVE_SLIDER constant, and maxNegative* state plus the useEffect that derives the absolute input from currency.maxNegativeBalance form a coherent flow and ensure the UI is initialized from the latest server value when the currency data loads or is refetched. No functional issues spotted here.

Also applies to: 18-18, 25-28, 56-64

@xPathin xPathin merged commit dd226ce into main Dec 15, 2025
4 checks passed
@coodos coodos deleted the feat/burn-and-negative-control branch December 15, 2025 09:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feature] Ability to Burn money [feature] Implement Negative Limit [feature] Hashing and Timestamps

4 participants